package com.aps.server.kpi.service;

import com.aps.algorithm.entity.BasicEquipSchedulingResult;
import com.aps.common.model.genetic.ProcessInterval;
import com.aps.common.service.TestGenericService;
import com.aps.server.entity.ApsOperation;
import com.aps.server.kpi.GenericDefaultKpiTemplateEngine;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.Duration;
import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * @Author nbZhuozh
 * @Date 2025-11-12  16:57
 **/
@Service
@Slf4j
/**
 * 设备相关 详细维度需要从 3各维度计算
 * 1、时间
 * 2、性能
 * 3、质检
 * 最终目的： 计算每个设备的 OEE  OEE计算标准 = 时间开动率 * 性能开动率 * 产品合格率
 * OEE世界衡量标准 ： 85%以上：标杆企业  60%以上 普通水平  40%以下 水平较差，急需改善
 */
public class EquipKpiCalculatedService extends TestGenericService {


    /**
     * 基于产能的 各设备性能开动率
     * 公式： 设备性能开动率 = （标准工时 / 计划中实际占用时间） * 100%
     * 拆分：标准工时 = 对某一台设备，汇总其 计划中所有的生产的产品数量 ， * 该产品的单件标准工时 。 计划占用时间：计划中使用该设备的 工序的时长（需要计算准备时长）
     * 目的：反应设备在作业时间内，相对于其标准（产能的运行效率）
     * @param reference
     * @return
     */
    public Map<String , BigDecimal> calEquipmentCapacityRate(GenericDefaultKpiTemplateEngine.KpiBizReference reference){
        Map<String , BigDecimal> result = new HashMap<>();
        //计划
        List<BasicEquipSchedulingResult> basicEquipSchedulingResultList = reference.getBasicEquipSchedulingResultList();
        if(!CollectionUtils.isEmpty(basicEquipSchedulingResultList)){
            Map<String, List<BasicEquipSchedulingResult>> equipCollectMap = basicEquipSchedulingResultList.stream()
                    .collect(Collectors.groupingBy(BasicEquipSchedulingResult::getEquipId));
            //计算各设备在计划中的标准工时
            equipCollectMap.forEach((k,v)->{
                //设备在计划中的标准工时
                BigDecimal standardWorkingTime = calEquipStandardWorkingTime(k,reference,v);
                //设备在计划中占用的时间
                BigDecimal equipworkingTime = calEquipWorkingTime(k,reference,v);
                if(equipworkingTime.compareTo(BigDecimal.ZERO) ==0){
                    result.put(k,BigDecimal.ZERO);
                }
                result.put(k,standardWorkingTime.divide(equipworkingTime).setScale(2, RoundingMode.HALF_UP));
            });
        }
        return result;

    }

    //基于产能的 平均设备利用率
    public BigDecimal calAvgEquipmentCapacityRate(GenericDefaultKpiTemplateEngine.KpiBizReference reference){
        Map<String, BigDecimal> stringBigDecimalMap = calEquipmentCapacityRate(reference);
        if(CollectionUtils.isEmpty(stringBigDecimalMap)){
            return BigDecimal.ZERO;
        }
        //累加 / size  取均值
        return stringBigDecimalMap
                .values()
                .stream()
                .reduce(BigDecimal.ZERO, BigDecimal::add)
                .divide(new BigDecimal(stringBigDecimalMap.size()), 2, RoundingMode.HALF_UP);
    }

    //整体设备负载均衡度

    //设备生产效率

    //平均设备生产效率

    /**
     * 计算基于所有订单（产品）的 设备标准工时
     * 标准工时 = 对某一台设备，汇总其 计划中所有的生产的产品数量 * 该订单（产品）的单件标准工时
     *
     * 处理逻辑：1、找到对应产品 2、找到对应工序 3、使用Sum(工序i标准工时 * 工序i产品数量)
     * @param equipId
     * @param basicEquipSchedulingResultList
     * @return
     */
    private BigDecimal calEquipStandardWorkingTime(String equipId, GenericDefaultKpiTemplateEngine.KpiBizReference reference ,  List<BasicEquipSchedulingResult> basicEquipSchedulingResultList){
        Map<String, ApsOperation> basicOperationMap = reference.getBasicOperationMap();
        BigDecimal res = new BigDecimal(0);
        for (BasicEquipSchedulingResult basicEquipSchedulingResult : basicEquipSchedulingResultList) {
            String processIntervalJsonStr = basicEquipSchedulingResult.getProcessIntervalJsonStr();
            if(StringUtils.isEmpty(processIntervalJsonStr)){
               continue;
            }
            List<ProcessInterval> processIntervals = GenericDefaultKpiTemplateEngine.parseProcessIntervalsDetails(processIntervalJsonStr);
            //数量：
            int size = processIntervals.size();
            //获取工序标准工时
            ApsOperation apsOperation = basicOperationMap.get(basicEquipSchedulingResult.getProcessId());
            BigDecimal operationTotalStdTime = apsOperation.getOperationTotalStdTime();
            BigDecimal multiply = operationTotalStdTime.multiply(new BigDecimal(size));
            res = res.add(multiply);
        }
        return res;
    }

    /**
     * 计算设备占用时长
     * @param equipId
     * @param reference
     * @param basicEquipSchedulingResultList
     * @return
     */
    private BigDecimal calEquipWorkingTime(String equipId, GenericDefaultKpiTemplateEngine.KpiBizReference reference
            , List<BasicEquipSchedulingResult> basicEquipSchedulingResultList){
        BigDecimal res = new BigDecimal(0);
        Map<String, ApsOperation> basicOperationMap = reference.getBasicOperationMap();
        for (BasicEquipSchedulingResult basicEquipSchedulingResult : basicEquipSchedulingResultList) {
            //工序占用时长 = 工序准备时间 +  （工序计划结束时间 - 工序计划开始时间）
            LocalDateTime processStartTime = basicEquipSchedulingResult.getProcessStartTime();
            LocalDateTime processEndTime = basicEquipSchedulingResult.getProcessEndTime();
            String processId = basicEquipSchedulingResult.getProcessId();
            ApsOperation apsOperation = basicOperationMap.get(processId);
            BigDecimal operationPreparedTime = apsOperation.getOperationPreparedTime();
            long minutes = Duration.between(processStartTime, processEndTime).toMinutes();
            res = res.add(new BigDecimal(minutes)).add(operationPreparedTime);
        }
        return res;
    }



}
